﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace fm2
{
	public enum StatusElement { OK, Existing, Created, Changed, Deleted }
	public interface INodeElement
	{
		StatusElement Status { get; set; }
		bool _Selected { get; set; }
	}
	public interface IItemElement<T> : INodeElement
	{
		void CopyFrom(T item);
		bool _AddToLog { get; set; }
	}

	public class Node<TNode, TItem>
		where TNode : INodeElement, IComparable<TNode>
		where TItem : IItemElement<TItem>, IComparable<TItem>
	{
		private readonly Node<TNode, TItem> _parent;
		public Node<TNode, TItem> Parent { get { return _parent; } }

		private readonly TNode _value;
		public TNode Value { get { return _value; } }

		private readonly List<Node<TNode, TItem>> _nodes = new List<Node<TNode, TItem>>();
		public ReadOnlyCollection<Node<TNode, TItem>> Nodes { get { return _nodes.AsReadOnly(); } }

		private readonly List<TItem> _items = new List<TItem>();
		public ReadOnlyCollection<TItem> Items { get { return _items.AsReadOnly(); } }

		public Node()
		{
			_value = default(TNode);
			_parent = null;
		}

		public Node(TNode val, Node<TNode, TItem> parent = null)
		{
			_parent = parent;
			_value = val;
		}

		public Node<TNode, TItem> AddNode(TNode name)
		{
			Node<TNode, TItem> node = new Node<TNode, TItem>(name, this);
			_nodes.Add(node);
			return node;
		}

		public void AddItem(TItem item)
		{
			_items.Add(item);
		}

		public void Clear()
		{
			_nodes.Clear();
			_items.Clear();
		}

		public void SetStatus(StatusElement status)
		{
			_items.ForEach( item => item.Status = status );
			_nodes.ForEach(
				node => {
					node.Value.Status = status;
					node.SetStatus(status);
				});
		}

		public void RemoveDeleted()
		{
			_items.RemoveAll( item => item.Status == StatusElement.Deleted );
			_nodes.ForEach( node => node.RemoveDeleted() );
			_nodes.RemoveAll( node => node.Value.Status == StatusElement.Deleted );
		}

		public void Synchronize(Node<TNode, TItem> snode)
		{
			bool miss;
			int result;
			foreach(TItem _item in _items) {
				miss = true;
				_item._AddToLog = true;
				foreach(TItem item in snode._items.Where(m => m.Status == StatusElement.Created)) {
					result = _item.CompareTo(item);
					if (result == 0) {
						miss = false;
						_item._AddToLog = false;
						item.Status = StatusElement.Existing;
						break;
					} else if (result > 0) {
						miss = false;
						_item.Status = item.Status = StatusElement.Changed;
						_item.CopyFrom(item);
						break;
					}
				}
				if (miss) {
					_item.Status = StatusElement.Deleted;
				}
			}
			_items.AddRange(snode._items.Where(m => m.Status == StatusElement.Created));

			foreach(Node<TNode, TItem> _node in _nodes) {
				_node.Value.Status = StatusElement.Deleted;
				foreach(Node<TNode, TItem> node in snode._nodes.Where(m => m.Value.Status == StatusElement.Created)) {
					if (_node.Value.CompareTo(node.Value) == 0) {
						_node.Value.Status = node.Value.Status = StatusElement.OK;
						_node.Synchronize(node);
						break;
					}
				}
				if (_node.Value.Status == StatusElement.Deleted) {
					_node.SetStatus(StatusElement.Deleted);
				}
			}
			_nodes.AddRange(snode._nodes.Where(m => m.Value.Status == StatusElement.Created));
		}

		public override string ToString()
		{
			return Value.ToString();
		}

	}
}
